package com.wangyadong.hobby.thread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Exchanger;

public class ExchangerDemo {
    private final static Exchanger<DataBuffer> exchanger = new Exchanger<DataBuffer>();
    private final static DataBuffer initialEmptyBuffer = new DataBuffer();
    private final static DataBuffer initialFullBuffer = new DataBuffer("I");

    public static void main(String[] args) {
        class FillingLoop implements Runnable {
            int count = 0;

            @Override
            public void run() {
                DataBuffer currentBuffer = initialEmptyBuffer;
                try {
                    while (true) {
                        addToBuffer(currentBuffer);
                        if (currentBuffer.isFull()) {
                            System.out.println(Thread.currentThread().getName() + " " + "filling thread wants to exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                            currentBuffer = exchanger.exchange(currentBuffer);
                            System.out.println(Thread.currentThread().getName() + " " + "filling thread receives exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                            System.out.println(Thread.currentThread().getName() + " -----------------------------------------------------------------------------------------------");
                            currentBuffer = exchanger.exchange(currentBuffer);
                            System.out.println(Thread.currentThread().getName() + " " + "filling thread receives exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                        }
                    }
                } catch (InterruptedException ie) {
                    System.out.println("filling thread interrupted");
                }
            }

            void addToBuffer(DataBuffer buffer) {
                String item = "NI" + count++;
                System.out.println(Thread.currentThread().getName() + " " + "Adding: " + item);
                buffer.add(item);
            }
        }
        class EmptyingLoop implements Runnable {
            @Override
            public void run() {
                DataBuffer currentBuffer = initialFullBuffer;
                try {
                    while (true) {
                        takeFromBuffer(currentBuffer);
                        if (currentBuffer.isEmpty()) {
                            System.out.println(Thread.currentThread().getName() + " " + "emptying thread wants to " +
                                    "exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                            currentBuffer = exchanger.exchange(currentBuffer);
                            System.out.println(Thread.currentThread().getName() + " " + "emptying thread receives " +
                                    "exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                            System.out.println(Thread.currentThread().getName() + " -----------------------------------------------------------------------------------------------");
                            currentBuffer = exchanger.exchange(currentBuffer);
                            System.out.println(Thread.currentThread().getName() + " " + "emptying thread receives " +
                                    "exchange" + " currentBuffer hashcode is " + currentBuffer.hashCode());
                        }
                    }
                } catch (InterruptedException ie) {
                    System.out.println("emptying thread interrupted");
                }
            }

            void takeFromBuffer(DataBuffer buffer) {
                System.out.println(Thread.currentThread().getName() + " " + "taking: " + buffer.remove());
            }
        }
        new Thread(new EmptyingLoop(), "emptyThread-").start();
        new Thread(new FillingLoop(), "fillThread-").start();
    }
}

class DataBuffer {
    private final static int MAXITEMS = 10;
    private final List<String> items = new ArrayList<>();

    DataBuffer() {
    }

    DataBuffer(String prefix) {
        for (int i = 0; i < MAXITEMS; i++) {
            String item = prefix + i;
            System.out.printf("Adding %s%n", item);
            items.add(item);
        }
    }

    synchronized void add(String s) {
        if (!isFull())
            items.add(s);
    }

    synchronized boolean isEmpty() {
        return items.size() == 0;
    }


    synchronized boolean isFull() {
        return items.size() == MAXITEMS;
    }

    synchronized String remove() {
        if (!isEmpty())
            return items.remove(0);
        return null;
    }
}